/**
 *******************************************************************************
 * @file  hc32f160_clk.c
 * @brief This file provides firmware functions to manage the Clock(CLK).
 @verbatim
   Change Logs:
   Date             Author          Notes
   2020-11-27       CDT             First version
 @endverbatim
 *******************************************************************************
 * Copyright (C) 2020, Huada Semiconductor Co., Ltd. All rights reserved.
 *
 * This software component is licensed by HDSC under BSD 3-Clause license
 * (the "License"); You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at:
 *                    opensource.org/licenses/BSD-3-Clause
 *
 *******************************************************************************
 */

/*******************************************************************************
 * Include files
 ******************************************************************************/
#include "hc32f160_clk.h"
#include "hc32f160_utility.h"

/**
 * @addtogroup HC32F160_DDL_Driver
 * @{
 */

/**
 * @defgroup DDL_CLK CLK
 * @brief Clock Driver Library
 * @{
 */

#if (DDL_CLK_ENABLE == DDL_ON)

/*******************************************************************************
 * Local type definitions ('typedef')
 ******************************************************************************/

/*******************************************************************************
 * Local pre-processor symbols/macros ('#define')
 ******************************************************************************/
/**
 * @defgroup CLK_Local_Macros CLK Local Macros
 * @{
 */

#define CLK_TIMEOUT             (0x1000U)
#define CLK_XTAL_TIMEOUT        (0x20000UL)


/**
 * @defgroup CLK_Check_Parameters_Validity CLK Check Parameters Validity
 * @{
 */
/*  Check CLK register lock status. */
#define IS_CLK_UNLOCK()       ((CM_PWC->FPRC & PWC_FPRC_CKRWE) == PWC_FPRC_CKRWE)
#define IS_CLK_EFM_UNLOCK()   (CM_EFM->FAPRT == 0x00000001UL)

/* parameter valid check for XTAL state */
#define IS_CLK_XTAL_STAT(x)                                                    \
(   ((x) == CLK_XTAL_OFF)                           ||                         \
    ((x) == CLK_XTAL_ON))

/* parameter valid check for XTAL mode */
#define IS_CLK_XTAL_MD(x)                                                      \
(   ((x) == CLK_XTAL_MD_OSC)                        ||                         \
    ((x) == CLK_XTAL_MD_EXT_CLK))

/* parameter valid check for XTAL super high speed driver state */
#define IS_CLK_XTAL_SUPDRV_STAT(x)                                             \
(   ((x) == CLK_XTAL_SUPDRV_ON)                     ||                         \
    ((x) == CLK_XTAL_SUPDRV_OFF))

/* parameter valid check for XTAL driver ability mode */
#define IS_CLK_XTAL_DRV_MD(x)                                                  \
(   ((x) == CLK_XTAL_DRV_HIGH)                      ||                         \
    ((x) == CLK_XTAL_DRV_MID)                       ||                         \
    ((x) == CLK_XTAL_DRV_LOW)                       ||                         \
    ((x) == CLK_XTAL_DRV_ULOW))

/* parameter valid check for XTAL stable time selection */
#define IS_CLK_XTALSTB_SEL(x)                                                  \
(   ((x) == CLK_XTALSTB_2POW8)                      ||                         \
    ((x) == CLK_XTALSTB_2POW9 )                     ||                         \
    ((x) == CLK_XTALSTB_2POW10)                     ||                         \
    ((x) == CLK_XTALSTB_2POW11)                     ||                         \
    ((x) == CLK_XTALSTB_2POW13)                     ||                         \
    ((x) == CLK_XTALSTB_2POW15)                     ||                         \
    ((x) == CLK_XTALSTB_2POW16)                     ||                         \
    ((x) == CLK_XTALSTB_2POW17))

/* parameter valid check for XTALSTD state */
#define IS_CLK_XTALSTD_STAT(x)                                                 \
(   ((x) == CLK_XTALSTD_OFF)                        ||                         \
    ((x) == CLK_XTALSTD_ON))

/* parameter valid check for XTALSTD mode */
#define IS_CLK_XTALSTD_EXP_TYPE(x)                                             \
(   ((x) == CLK_XTALSTD_EXP_TYPE_RST)               ||                         \
    ((x) == CLK_XTALSTD_EXP_TYPE_INT))

/* parameter valid check for XTALSTD interrupt state */
#define IS_CLK_XTALSTD_INT_STAT(x)                                             \
(   ((x) == CLK_XTALSTD_INT_OFF)                    ||                         \
    ((x) == CLK_XTALSTD_INT_ON))

/* parameter valid check for XTALSTD reset state */
#define IS_CLK_XTALSTD_RST_STAT(x)                                             \
(   ((x) == CLK_XTALSTD_RST_OFF)                    ||                         \
    ((x) == CLK_XTALSTD_RST_ON))

/* parameter valid check for HRC state */
#define IS_CLK_HRC_STAT(x)                                                     \
(   ((x) == CLK_HRC_OFF)                            ||                         \
    ((x) == CLK_HRC_ON))

/* parameter valid check for HRC state */
#define IS_CLK_HRC_FREQ_SEL(x)                                                 \
(   ((x) == CLK_HRCFREQ_64M)                        ||                         \
    ((x) == CLK_HRCFREQ_32M)                        ||                         \
    ((x) == CLK_HRCFREQ_16M)                        ||                         \
    ((x) == CLK_HRCFREQ_8M)                         ||                         \
    ((x) == CLK_HRCFREQ_4M)                         ||                         \
    ((x) == CLK_HRCFREQ_2M))

/* parameter valid check for XTAL32 state */
#define IS_CLK_XTAL32_STAT(x)                                                  \
(   ((x) == CLK_XTAL32_OFF)                         ||                         \
    ((x) == CLK_XTAL32_ON))

/* parameter valid check for XTAL32 driver ability mode */
#define IS_CLK_XTAL32_DRV_MD(x)                                                \
(   ((x) == CLK_XTAL32_DRV_MID)                     ||                         \
    ((x) == CLK_XTAL32_DRV_HIGH)                    ||                         \
    ((x) == CLK_XTAL32_DRV_LOW)                     ||                         \
    ((x) == CLK_XTAL32_DRV_ULOW))

/* parameter valid check for XTAL32 filtering selection */
#define IS_CLK_XTAL32_FILT_SEL(x)                                              \
(   ((x) == CLK_XTAL32_NF_ALL)                      ||                         \
    ((x) == CLK_XTAL32_NF_RUN)                      ||                         \
    ((x) == CLK_XTAL32_NF_NONE))

/* parameter valid check for HRC frequency selection */
#define IS_CLK_HRCFREQ_SEL()                                                   \
(   ((x) == CLK_HRCFREQ_32M)                        ||                         \
    ((x) == CLK_HRCFREQ_16M)                        ||                         \
    ((x) == CLK_HRCFREQ_8M)                         ||                         \
    ((x) == CLK_HRCFREQ_4M)                         ||                         \
    ((x) == CLK_HRCFREQ_2M)                         ||                         \
    ((x) == CLK_HRCFREQ_1M)

/* parameter valid check for system clock source */
#define IS_CLK_SYSCLK_SRC(x)                                                   \
(   ((x) == CLK_SYSCLK_SRC_HRC)                     ||                         \
    ((x) == CLK_SYSCLK_SRC_XTAL)                    ||                         \
    ((x) == CLK_SYSCLK_SRC_LRC)                     ||                         \
    ((x) == CLK_SYSCLK_SRC_XTAL32))

/* parameter valid check for system clock divider */
#define IS_CLK_SYSCLK_DIV(x)                                                   \
(   ((x) == CLK_SYSCLK_DIV1)                        ||                         \
    ((x) == CLK_SYSCLK_DIV2)                        ||                         \
    ((x) == CLK_SYSCLK_DIV4)                        ||                         \
    ((x) == CLK_SYSCLK_DIV8)                        ||                         \
    ((x) == CLK_SYSCLK_DIV16)                       ||                         \
    ((x) == CLK_SYSCLK_DIV32)                       ||                         \
    ((x) == CLK_SYSCLK_DIV64))

/* parameter valid check for hclk divider */
#define IS_VALID_HCLK_DIV(x)                                                   \
(   ((x) == CLK_HCLK_DIV1)                          ||                         \
    ((x) == CLK_HCLK_DIV2)                          ||                         \
    ((x) == CLK_HCLK_DIV4)                          ||                         \
    ((x) == CLK_HCLK_DIV8)                          ||                         \
    ((x) == CLK_HCLK_DIV16)                         ||                         \
    ((x) == CLK_HCLK_DIV32))

/* Parameter valid check for CLK MCO clock source. */
#define IS_CLK_MCO_SRC(x)                                                      \
(   ((x) == CLK_MCO_SRC_HRC)                        ||                         \
    ((x) == CLK_MCO_SRC_LRC)                        ||                         \
    ((x) == CLK_MCO_SRC_XTAL)                       ||                         \
    ((x) == CLK_MCO_SRC_XTAL32)                     ||                         \
    ((x) == CLK_MCO_SRC_SYSCLK))

/* Parameter valid check for CLK MCO clock Prescaler. */
#define IS_CLK_MCO_DIV(x)                                                      \
(   ((x) == CLK_MCO_DIV1)                           ||                         \
    ((x) == CLK_MCO_DIV2)                           ||                         \
    ((x) == CLK_MCO_DIV4)                           ||                         \
    ((x) == CLK_MCO_DIV8)                           ||                         \
    ((x) == CLK_MCO_DIV16)                          ||                         \
    ((x) == CLK_MCO_DIV32)                          ||                         \
    ((x) == CLK_MCO_DIV64)                          ||                         \
    ((x) == CLK_MCO_DIV128))

/* Parameter valid check for CLK stable flag. */
#define IS_CLK_STB_FLAG(x)                                                     \
(   ((x) == CLK_FLAG_HRCSTB)                        ||                         \
    ((x) == CLK_FLAG_XTALSTB)                       ||                         \
    ((x) == CLK_FLAG_XTAL32STB))
/**
 * @}
 */

/**
 * @}
 */

/*******************************************************************************
 * Global variable definitions (declared in header file with 'extern')
 ******************************************************************************/

/*******************************************************************************
 * Local function prototypes ('static')
 ******************************************************************************/

/*******************************************************************************
 * Local variable definitions ('static')
 ******************************************************************************/

/*******************************************************************************
 * Function implementation - global ('extern') and local ('static')
 ******************************************************************************/
/**
 * @defgroup CLK_Global_Functions CLK Global Functions
 * @{
 */
/**
  * @brief  Resets the CLK clock configuration to the default reset state.
  * @note   The default reset state of the clock configuration is given below:
  *            - LRC HRC ON and HRC used as system clock source
  *            - XTAL XTAL32 OFF
  *            - SysClk divider set to 1.
  *            - MCO OFF
  *            - All interrupts disabled
  * @param  None
  * @retval None
  */
void CLK_DeInit(void)
{
    DDL_ASSERT(IS_CLK_UNLOCK());

    /* Reset peripheral clock */
    WRITE_REG32(CM_CMU->FCG, 0xFFFFFFFFUL);

    /* Reset system clock / HCLK divider */
    WRITE_REG8(CM_CMU->SCKDIVR, 0x00U);
    WRITE_REG8(CM_CMU->PERICKSEL, 0x00U);

    /* Reset MCO */
    WRITE_REG8(CM_CMU->MCO1CFGR, 0x00U);

    /* Reset XTALSTD XTALCFGR */
    WRITE_REG8(CM_CMU->XTALSTDCR, 0x00U);
    WRITE_REG8(CM_CMU->XTALCFGR, 0x80U);

    /* Reset Lrc Hrc Xtal XTAL32 */
    WRITE_REG8(CM_CMU->LRCCR, 0x00U);
    WRITE_REG8(CM_CMU->HRCCR, 0x00U);
    WRITE_REG8(CM_CMU->XTALCR, 0x01U);
    WRITE_REG8(CM_CMU->XTAL32CR, 0x01U);

    /* Reset the system source */
    WRITE_REG8(CM_CMU->CKSWR, 0x00U);

    /* Update system clock */
    SystemCoreClockUpdate();
}

/**
 * @brief  Init Xtal initial structure with default value.
 * @param  [in] pstcXtalInit        Specifies the parameter of Xtal.
 *   @arg  u8XtalState  : The new state of the Xtal.
 *   @arg  u8XtalMode   : The Xtal mode selection osc or exclk.
 *   @arg  u8XtalDrv    : The Xtal drive ability.
 *   @arg  u8XtalSupDrv : The Xtal super drive on or off.
 *   @arg  u8XtalStb    : The Xtal stable time selection.
 * @retval An en_result_t enumeration value:
 *   @arg  Ok: Initialize success
 *   @arg  ErrorInvalidParameter: Invalid parameter
 */
en_result_t CLK_XtalStructInit(stc_clock_xtal_init_t* pstcXtalInit)
{
    en_result_t enRet = Ok;

    /* Check if pointer is NULL */
    if (NULL == pstcXtalInit)
    {
        enRet = ErrorInvalidParameter;
    }
    else
    {
        /* Configure to default value */
        pstcXtalInit->u8XtalState   = CLK_XTAL_OFF;
        pstcXtalInit->u8XtalMode    = CLK_XTAL_MD_OSC;
        pstcXtalInit->u8XtalDrv     = CLK_XTAL_DRV_HIGH;
        pstcXtalInit->u8XtalSupDrv  = CLK_XTAL_SUPDRV_ON;
        pstcXtalInit->u8XtalStb     = CLK_XTALSTB_2POW8;
    }

    return enRet;
}

/**
  * @brief  Initialise the Xtal.
  * @param  [in] pstcXtalInit       Specifies the parameter of Xtal.
  *   @arg  u8XtalState  : The new state of the Xtal.
  *   @arg  u8XtalMode   : The Xtal mode selection osc or exclk.
  *   @arg  u8XtalDrv    : The Xtal drive ability.
  *   @arg  u8XtalSupDrv : The Xtal super drive on or off.
  *   @arg  u8XtalStb    : The Xtal stable time selection.
  * @retval en_result_t Status
  */
en_result_t CLK_XtalInit(const stc_clock_xtal_init_t* pstcXtalInit)
{
    en_result_t enRet;

    if (pstcXtalInit == NULL)
    {
        enRet = ErrorInvalidParameter;
    }
    else
    {
        /* parameters check */
        DDL_ASSERT(IS_CLK_XTAL_STAT(pstcXtalInit->u8XtalState));
        DDL_ASSERT(IS_CLK_XTAL_MD(pstcXtalInit->u8XtalMode));
        DDL_ASSERT(IS_CLK_XTAL_DRV_MD(pstcXtalInit->u8XtalDrv));
        DDL_ASSERT(IS_CLK_XTAL_SUPDRV_STAT(pstcXtalInit->u8XtalSupDrv));
        DDL_ASSERT(IS_CLK_XTALSTB_SEL(pstcXtalInit->u8XtalStb));
        DDL_ASSERT(IS_CLK_UNLOCK());

        /* Configure XTAL stable time */
        WRITE_REG8(CM_CMU->XTALSTBCR, pstcXtalInit->u8XtalStb);

        /* Configure XTAL mode, driver ability, super_driver */
        WRITE_REG8(CM_CMU->XTALCFGR, (pstcXtalInit->u8XtalDrv   | \
                                      pstcXtalInit->u8XtalMode  | \
                                      pstcXtalInit->u8XtalSupDrv));

        if (CLK_XTAL_OFF == pstcXtalInit->u8XtalState)
        {
            enRet = CLK_XtalCmd(Disable);
        }
        else
        {
            enRet = CLK_XtalCmd(Enable);
        }
    }

    return enRet;
}

/**
 * @brief  Xtal function enable/disbale.
 * @param  [in] enNewState          Specifies the new state of Xtal.
 *   @arg  Enable        : Enable Xtal.
 *   @arg  Disable       : Disable Xtal.
 * @retval en_result_t
 *   @arg  Ok            : Xtal operate successfully
 *   @arg  ErrorNotReady : Can not stop Xtal while using xtal as system clock.
 *   @arg  ErrorTimeout  : Xtal operate timeout.
 * @note   DO NOT STOP Xtal while using it as system clock.
 */
en_result_t CLK_XtalCmd(en_functional_state_t enNewState)
{
    en_result_t enRet = Ok;
    __IO uint32_t u32timeout = 0UL;

    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));
    DDL_ASSERT(IS_CLK_UNLOCK());

    if (Disable == enNewState)
    {
        if (CLK_SYSCLK_SRC_XTAL == READ_REG8_BIT(CM_CMU->CKSWR, CMU_CKSWR_CKSW))
        {
            enRet = ErrorNotReady;
        }
        else
        {
            /* Disable Xtal */
            WRITE_REG8(CM_CMU->XTALCR, CLK_XTAL_OFF);
        }
    }
    else
    {
        /* Enable Xtal */
        WRITE_REG8(CM_CMU->XTALCR, CLK_XTAL_ON);
        /* Wait XTAL stable */
        while (CLK_FLAG_XTALSTB != READ_REG8_BIT(CM_CMU->OSCSTBSR, CMU_OSCSTBSR_XTALSTBF))
        {
            u32timeout++;
            if (u32timeout > CLK_XTAL_TIMEOUT)
            {
                enRet = ErrorTimeout;
                break;
            }
        }
    }

    return enRet;
}

/**
 * @brief  Init Xtal32 initial structure with default value.
 * @param  [in] pstcXtal32Init      Specifies the parameter of Xtal32.
 *   @arg  u8Xtal32State  : The new state of the XTAL32.
 *   @arg  u8Xtal32Drv    : The Xtal32 mode selection osc or exclk.
 *   @arg  u8Xtal32SupDrv : The Xtal32 drive ability.
 *   @arg  u8Xtal32NF     : The Xtal32 super drive on or off.
 * @retval An en_result_t enumeration value:
 *   @arg  Ok                    : Initialize success
 *   @arg  ErrorInvalidParameter : Invalid parameter
 */
en_result_t CLK_Xtal32StructInit(stc_clock_xtal32_init_t* pstcXtal32Init)
{
    en_result_t enRet = Ok;

    /* Check if pointer is NULL */
    if (NULL == pstcXtal32Init)
    {
        enRet = ErrorInvalidParameter;
    }
    else
    {
        /* Configure to default value */
        pstcXtal32Init->u8Xtal32State   = CLK_XTAL32_OFF;
        pstcXtal32Init->u8Xtal32Drv     = CLK_XTAL32_DRV_MID;
        pstcXtal32Init->u8Xtal32NF      = CLK_XTAL32_NF_ALL;
    }

    return enRet;
}

/**
 * @brief  Initialise the Xtal32.
 * @param  [in] pstcXtal32Init          Specifies the parameter of Xtal32.
 *   @arg  u8Xtal32State  : The new state of the Xtal32.
 *   @arg  u8Xtal32Drv    : The Xtal32 mode selection osc or exclk.
 *   @arg  u8Xtal32SupDrv : The Xtal32 drive ability.
 *   @arg  u8Xtal32NF     : The Xtal32 super drive on or off.
 * @retval en_result_t Status
 */
en_result_t CLK_Xtal32Init(const stc_clock_xtal32_init_t* pstcXtal32Init)
{
    en_result_t enRet;

    if (pstcXtal32Init == NULL)
    {
        enRet = ErrorInvalidParameter;
    }
    else
    {
        /* parameters check */
        DDL_ASSERT(IS_CLK_XTAL32_STAT(pstcXtal32Init->u8Xtal32State));
        DDL_ASSERT(IS_CLK_XTAL32_DRV_MD(pstcXtal32Init->u8Xtal32Drv));
        DDL_ASSERT(IS_CLK_XTAL32_FILT_SEL(pstcXtal32Init->u8Xtal32NF));
        DDL_ASSERT(IS_CLK_UNLOCK());

        WRITE_REG8(CM_CMU->XTAL32CFGR, pstcXtal32Init->u8Xtal32Drv);
        WRITE_REG8(CM_CMU->XTAL32NFR, pstcXtal32Init->u8Xtal32NF);

        if(CLK_XTAL32_OFF == pstcXtal32Init->u8Xtal32State)
        {
            enRet = CLK_Xtal32Cmd(Disable);
        }
        else
        {
            enRet = CLK_Xtal32Cmd(Enable);
        }
    }

    return enRet;
}

/**
 * @brief  Xtal32 function enable/disbale.
 * @param  [in] enNewState          Specifies the new state of Xtal32.
 *   @arg  Enable        : Enable Xtal32.
 *   @arg  Disable       : Disable Xtal32.
 * @retval en_result_t
 *   @arg  Ok            : Xtal32 operate successfully
 *   @arg  ErrorNotReady : Can not stop Xtal32 while using xtal32 as system clock.
 *   @arg  ErrorTimeout  : Xtal32 operate timeout.
 * @note   DO NOT STOP Xtal32 while using it as system clock.
 */
en_result_t CLK_Xtal32Cmd(en_functional_state_t enNewState)
{
    en_result_t enRet = Ok;
    __IO uint32_t u32timeout = 0UL;

    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));
    DDL_ASSERT(IS_CLK_UNLOCK());

    if (Disable == enNewState)
    {
        if (CLK_SYSCLK_SRC_XTAL32 == READ_REG8_BIT(CM_CMU->CKSWR, CMU_CKSWR_CKSW))
        {
            enRet = ErrorNotReady;
        }
        else
        {
            /* Disable Xtal32 */
            WRITE_REG8(CM_CMU->XTAL32CR, CLK_XTAL32_OFF);
        }
    }
    else
    {
        /* Enable Xtal32 */
        WRITE_REG8(CM_CMU->XTAL32CR, CLK_XTAL32_ON);
        /* Wait XTAL32 stable */
        while(CLK_FLAG_XTAL32STB != READ_REG8_BIT(CM_CMU->OSCSTBSR, CMU_OSCSTBSR_XTAL32STBF))
        {
            u32timeout++;
            if(u32timeout > CLK_TIMEOUT)
            {
                enRet = ErrorTimeout;
                break;
            }
        }
    }

    return enRet;
}

/**
 * @brief  Configure the Hrc frequency.
 * @param  [in] u8HrcFreq           Specifies the frequency of HRC.
 *           This parameter can be one of the following values:
 *   @arg  CLK_HRCFREQ_64M : HRC frequency is 32MHz while ICG.HRCFREQS[3] = 0.
 *   @arg  CLK_HRCFREQ_32M : HRC frequency is 16MHz while ICG.HRCFREQS[3] = 0.
 *   @arg  CLK_HRCFREQ_16M : HRC frequency is 8MHz while ICG.HRCFREQS[3] = 0.
 *   @arg  CLK_HRCFREQ_8M  : HRC frequency is 4MHz while ICG.HRCFREQS[3] = 0.
 *   @arg  CLK_HRCFREQ_4M  : HRC frequency is 2MHz while ICG.HRCFREQS[3] = 0.
 *   @arg  CLK_HRCFREQ_2M  : HRC frequency is 1MHz while ICG.HRCFREQS[3] = 0.
 *   @arg  CLK_HRCFREQ_48M : HRC frequency is 48MHz while ICG.HRCFREQS[3] = 1.
 *   @arg  CLK_HRCFREQ_24M : HRC frequency is 24MHz while ICG.HRCFREQS[3] = 1.
 *   @arg  CLK_HRCFREQ_12M : HRC frequency is 12MHz while ICG.HRCFREQS[3] = 1.
 *   @arg  CLK_HRCFREQ_6M  : HRC frequency is 6MHz while ICG.HRCFREQS[3] = 1.
 *   @arg  CLK_HRCFREQ_3M  : HRC frequency is 3MHz while ICG.HRCFREQS[3] = 1.
 *   @arg  CLK_HRCFREQ_1P5M: HRC frequency is 1.5MHz while ICG.HRCFREQS[3] = 1.
 * @retval en_result_t Status
 */
en_result_t CLK_HrcFreqConfig(uint8_t u8HrcFreq)
{
    __IO uint32_t u32timeout = 0UL;
    en_result_t enRet = Ok;

    DDL_ASSERT(IS_CLK_HRC_FREQ_SEL(u8HrcFreq));
    DDL_ASSERT(IS_CLK_UNLOCK());
    DDL_ASSERT(IS_CLK_EFM_UNLOCK());

    /* Configure Hrc frequency */
    WRITE_REG8(CM_EFM->CMU_HRCCFGR, u8HrcFreq);

    /* Wait Hrc stable */
    while(CLK_FLAG_HRCSTB != READ_REG8_BIT(CM_CMU->OSCSTBSR, CMU_OSCSTBSR_HRCSTBF))
    {
        u32timeout++;
        if(u32timeout > CLK_TIMEOUT)
        {
            enRet = ErrorTimeout;
            break;
        }
    }

    /* Update system clock */
    SystemCoreClockUpdate();

    return enRet;
}

/**
 * @brief  Hrc function enable/disbale.
 * @param  [in] enNewState          Specifies the new state of Hrc.
 *   @arg  Enable        : Enable Hrc.
 *   @arg  Disable       : Disable Hrc.
 * @retval en_result_t
 *   @arg  Ok            : Hrc operate successfully
 *   @arg  ErrorNotReady : Can not stop Hrc while using Hrc as system clock.
 *   @arg  ErrorTimeout  : Hrc operate timeout.
 * @note   DO NOT STOP Hrc while using it as system clock.
 */
en_result_t CLK_HrcCmd(en_functional_state_t enNewState)
{
    en_result_t enRet = Ok;
    __IO uint32_t u32timeout = 0UL;

    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));
    DDL_ASSERT(IS_CLK_UNLOCK());

    if (Disable == enNewState)
    {
        if (CLK_SYSCLK_SRC_HRC == READ_REG8_BIT(CM_CMU->CKSWR, CMU_CKSWR_CKSW))
        {
            enRet = ErrorNotReady;
        }
        else
        {
            /* Disable Hrc */
            WRITE_REG8(CM_CMU->HRCCR, CLK_HRC_OFF);
        }
    }
    else
    {
        /* Enable Hrc */
        WRITE_REG8(CM_CMU->HRCCR, CLK_HRC_ON);
        /* Wait Hrc stable */
        while(CLK_FLAG_HRCSTB != READ_REG8_BIT(CM_CMU->OSCSTBSR, CMU_OSCSTBSR_HRCSTBF))
        {
            u32timeout++;
            if(u32timeout > CLK_TIMEOUT)
            {
                enRet = ErrorTimeout;
                break;
            }
        }
    }

    return enRet;
}

/**
 * @brief  Lrc function enable/disbale.
 * @param  [in] enNewState          Specifies the new state of Lrc.
 *   @arg  Enable        : Enable Lrc.
 *   @arg  Disable       : Disable Lrc.
 * @retval en_result_t
 *   @arg  Ok            : Lrc operate successfully
 *   @arg  ErrorNotReady : Can not stop Lrc while using Lrc as system clock.
 * @note   DO NOT STOP Lrc while using it as system clock.
 */
en_result_t CLK_LrcCmd(en_functional_state_t enNewState)
{
    en_result_t enRet = Ok;

    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));
    DDL_ASSERT(IS_CLK_UNLOCK());

    if (Disable == enNewState)
    {
        if (CLK_SYSCLK_SRC_LRC == READ_REG8_BIT(CM_CMU->CKSWR, CMU_CKSWR_CKSW))
        {
            enRet = ErrorNotReady;
        }
        else
        {
            /* Disable Lrc */
            WRITE_REG8(CM_CMU->LRCCR, CLK_LRC_OFF);
        }
    }
    else
    {
        /* Enable Lrc */
        WRITE_REG8(CM_CMU->LRCCR, CLK_LRC_OFF);
    }

    return enRet;
}

/**
 * @brief  Init XtalStd initial structure with default value.
 * @param  [in] pstcXtalStdInit     Specifies the parameter of XtalStd.
 *   @arg  u8XtalStdState : The new state of the XtalStd.
 *   @arg  u8XtalStdMode  : The Xtal status detection occur interrupt or reset.
 *   @arg  u8XtalStdInt   : The Xtal status detection interrupt on or off.
 *   @arg  u8XtalStdRst   : The Xtal status detection reset on or off.
 * @retval An en_result_t enumeration value:
 *   @arg  Ok                    : Initialize success
 *   @arg  ErrorInvalidParameter : Invalid parameter
 */
en_result_t CLK_XtalStdStructInit(stc_clock_xtalstd_init_t* pstcXtalStdInit)
{
    en_result_t enRet = Ok;

    /* Check if pointer is NULL */
    if (NULL == pstcXtalStdInit)
    {
        enRet = ErrorInvalidParameter;
    }
    else
    {
        /* Configure to default value */
        pstcXtalStdInit->u8XtalStdState          = CLK_XTALSTD_OFF;
        pstcXtalStdInit->u8XtalStdExceptionType  = CLK_XTALSTD_EXP_TYPE_INT;
        pstcXtalStdInit->u8XtalStdInt            = CLK_XTALSTD_RST_OFF;
        pstcXtalStdInit->u8XtalStdRst            = CLK_XTALSTD_INT_OFF;
    }

    return enRet;
}

/**
 * @brief  Initialise the Xtal status detection.
 * @param  [in] pstcXtalStdInit         Specifies the parameter of XtalStd.
 *   @arg  u8XtalStdState: The new state of the XtalStd.
 *   @arg  u8XtalStdMode : The Xtal status detection occur interrupt or reset.
 *   @arg  u8XtalStdInt  : The Xtal status detection interrupt on or off.
 *   @arg  u8XtalStdRst  : The Xtal status detection reset on or off.
 * @retval An en_result_t enumeration value:
 *   @arg  Ok                    : Initialize success
 *   @arg  ErrorInvalidParameter : Invalid parameter
 */
en_result_t CLK_XtalStdInit(const stc_clock_xtalstd_init_t* pstcXtalStdInit)
{
    en_result_t enRet = Ok;

    if (pstcXtalStdInit == NULL)
    {
        enRet = ErrorInvalidParameter;
    }
    else
    {
        /* parameter valid check */
        DDL_ASSERT(IS_CLK_XTALSTD_STAT(pstcXtalStdInit->u8XtalStdState));
        DDL_ASSERT(IS_CLK_UNLOCK());

        if(CLK_XTALSTD_OFF == pstcXtalStdInit->u8XtalStdState)
        {
            /* Disbale XTAL status detection */
            WRITE_REG32(bCM_CMU->XTALSTDCR_b.XTALSTDE, (uint32_t)CLK_XTALSTD_OFF);
        }
        else
        {
            /* parameter valid check */
            DDL_ASSERT(IS_CLK_XTALSTD_EXP_TYPE(pstcXtalStdInit->u8XtalStdExceptionType));
            DDL_ASSERT(IS_CLK_XTALSTD_INT_STAT(pstcXtalStdInit->u8XtalStdInt));
            DDL_ASSERT(IS_CLK_XTALSTD_RST_STAT(pstcXtalStdInit->u8XtalStdRst));

            /* Confiure XTALSTD and enable XTALSTD */
            WRITE_REG8(CM_CMU->XTALSTDCR, pstcXtalStdInit->u8XtalStdState           | \
                                          pstcXtalStdInit->u8XtalStdExceptionType   | \
                                          pstcXtalStdInit->u8XtalStdInt             | \
                                          pstcXtalStdInit->u8XtalStdRst);
        }
    }

    return enRet;
}

/**
 * @brief  Set the system clock source.
 * @param  [in] u8Src               Specifies the source of system clock.
 *         This parameter can be one of the following values:
 *   @arg  CLK_SYSCLK_SRC_HRC    : select Hrc as system clock source
 *   @arg  CLK_SYSCLK_SRC_XTAL   : select Xtal as system clock source
 *   @arg  CLK_SYSCLK_SRC_LRC    : select Lrc as system clock source
 *   @arg  CLK_SYSCLK_SRC_XTAL32 : select Xtal32 as system clock source
 * @retval None
 */
void CLK_SetSysClockSrc(uint8_t u8Src)
{
    DDL_ASSERT(IS_CLK_SYSCLK_SRC(u8Src));
    DDL_ASSERT(IS_CLK_UNLOCK());

    /* Set system clock source */
    WRITE_REG8(CM_CMU->CKSWR, u8Src);

    /* Update system clock */
    SystemCoreClockUpdate();
}

/**
 * @brief  Set the clock divider.
 * @param  [in] u8Div               Specifies the divider of clock.
 *         This parameter can be one of the following values:
 *   @arg  CLK_SYSCLK_DIV1 : system clock 1 divider
 *   @arg  CLK_SYSCLK_DIV2 : system clock 2 divider
 *   @arg  CLK_SYSCLK_DIV4 : system clock 4 divider
 *   @arg  CLK_SYSCLK_DIV8 : system clock 8 divider
 *   @arg  CLK_SYSCLK_DIV16: system clock 16 divider
 *   @arg  CLK_SYSCLK_DIV32: system clock 32 divider
 *   @arg  CLK_SYSCLK_DIV64: system clock 64 divider
 * @retval None
 */
void CLK_SetClockDiv(uint8_t u8Div)
{
    DDL_ASSERT(IS_CLK_SYSCLK_DIV(u8Div));
    DDL_ASSERT(IS_CLK_UNLOCK());

    /* Set system clock divider */
    WRITE_REG8(CM_CMU->SCKDIVR, u8Div);

    /* Update system clock */
    SystemCoreClockUpdate();
}

/**
 * @brief  Get the clock frequency.
 * @param  None
 * @retval uint8_t      The clock frequency.
 */
uint32_t CLK_GetClockFreq(void)
{
    uint32_t u32SysClockFreq = HRC_VALUE;

    /* Get system clock. */
    switch(READ_REG8_BIT(CM_CMU->CKSWR, CMU_CKSWR_CKSW))
    {
        case CLK_SYSCLK_SRC_HRC:
            u32SysClockFreq = HRC_VALUE;
            break;
        case CLK_SYSCLK_SRC_XTAL:
            u32SysClockFreq = XTAL_VALUE;
            break;
        case CLK_SYSCLK_SRC_LRC:
            u32SysClockFreq = LRC_VALUE;
            break;
        case CLK_SYSCLK_SRC_XTAL32:
            u32SysClockFreq = XTAL32_VALUE;
            break;
        default:
            break;
    }

    return u32SysClockFreq >> READ_REG32_BIT(CM_CMU->SCKDIVR, CMU_SCKDIVR_SCKDIV);
}

/**
 * @brief  Set the HCLK divider witch used as ADC clock.
 * @param  [in] u8ADClockDiv        Specifies the divider of HCLK.
 *         This parameter can be one of the following values:
 *   @arg  CLK_HCLK_DIV1 : HCLK 1 divider
 *   @arg  CLK_HCLK_DIV2 : HCLK 2 divider
 *   @arg  CLK_HCLK_DIV4 : HCLK 4 divider
 *   @arg  CLK_HCLK_DIV8 : HCLK 8 divider
 *   @arg  CLK_HCLK_DIV16: HCLK 16 divider
 *   @arg  CLK_HCLK_DIV32: HCLK 32 divider
 * @retval None
 */
void CLK_SetADClockDiv(uint8_t u8ADClockDiv)
{
    DDL_ASSERT(IS_VALID_HCLK_DIV(u8ADClockDiv));
    DDL_ASSERT(IS_CLK_UNLOCK());

    /* Set ADC clock divider */
    WRITE_REG8(CM_CMU->PERICKSEL, u8ADClockDiv);
}

/**
 * @brief  Enable or disable the FCG peripheral clock.
 * @note   After reset,the peripheral clock is disabled and the application
 *         software has to enable this clock before using it.
 * @param  [in] u32FcgPeriph        Specifies the peripheral in FCG. @ref CLK_FCG_Periph_Control.
 * @param  [in] enNewState          Specifies the new state of the clock output.
 *          This parameter can be one of the following values:
 *   @arg  Enable :  Enable clock output.
 *   @arg  Disable:  Disable clock output.
 * @retval None
 */
void CLK_FcgPeriphClockCmd(uint32_t u32FcgPeriph, en_functional_state_t enNewState)
{
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));
    DDL_ASSERT(IS_CLK_UNLOCK());

    if(Enable == enNewState)
    {
        CLEAR_REG32_BIT(CM_CMU->FCG, u32FcgPeriph);
    }
    else
    {
        SET_REG32_BIT(CM_CMU->FCG, u32FcgPeriph);
    }
}

/**
 * @brief  Selects the clock source to output on MCO pin.
 * @param  [in] u8MCOSrc            Specifies the clock source to output.
 *         This parameter can be one of the following values:
 *   @arg  CLK_MCO_SRC_HRC   :    Hrc clock selected as MCO source
 *   @arg  CLK_MCO_SRC_LRC   :    Lrc clock selected as MCO source
 *   @arg  CLK_MCO_SRC_XTAL  :    Xtal clock selected as MCO source
 *   @arg  CLK_MCO_SRC_XTAL32:    Xtal32 clock selected as MCO source
 *   @arg  CLK_MCO_SRC_SYSCLK:    System clock(SYSCLK) selected as MCO source
 * @param  [in] u8MCODiv            Specifies the MCO prescaler.
 *         This parameter can be one of the following values:
 *   @arg  CLK_MCO_DIV1  : No division applied to MCO clock
 *   @arg  CLK_MCO_DIV2  : Division by 2 applied to MCO clock
 *   @arg  CLK_MCO_DIV4  : Division by 4 applied to MCO clock
 *   @arg  CLK_MCO_DIV8  : Division by 8 applied to MCO clock
 *   @arg  CLK_MCO_DIV16 : Division by 16 applied to MCO clock
 *   @arg  CLK_MCO_DIV32 : Division by 32 applied to MCO clock
 *   @arg  CLK_MCO_DIV64 : Division by 64 applied to MCO clock
 *   @arg  CLK_MCO_DIV128: Division by 128 applied to MCO clock
 * @retval None
 * @note   MCO pin should be configured in alternate function mode.
 */
void CLK_MCOConfig(uint8_t u8MCOSrc, uint8_t u8MCODiv)
{
    /* Check the parameters. */
    DDL_ASSERT(IS_CLK_MCO_SRC(u8MCOSrc));
    DDL_ASSERT(IS_CLK_MCO_DIV(u8MCODiv));
    DDL_ASSERT(IS_CLK_UNLOCK());

    /* Configure the MCO */
    MODIFY_REG8(CM_CMU->MCO1CFGR,                            \
                (CMU_MCO1CFGR_MCO1SEL | CMU_MCO1CFGR_MCO1DIV), \
                (u8MCODiv | u8MCOSrc));
}

/**
 * @brief  Enable or disable the MCO.
 * @param  [in] enNewState          Specifies the new state of the clock output.
 *         This parameter can be one of the following values:
 *   @arg   Enable :  Enable clock output.
 *   @arg   Disable:  Disable clock output.
 * @retval None
 */
void CLK_MCOCmd(en_functional_state_t enNewState)
{
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));
    DDL_ASSERT(IS_CLK_UNLOCK());

    /* Enable or disable clock output. */
    WRITE_REG32(bCM_CMU->MCO1CFGR_b.MCO1EN, enNewState);
}

/**
 * @brief  Clear the XTAL error flag.
 * @param  None
 * @retval None
 * @note   The system clock should not be XTAL before call this function.
 */
void CLK_ClearXtalStdStatus(void)
{
    DDL_ASSERT(IS_CLK_UNLOCK());

    if(1UL == READ_REG32(bCM_CMU->XTALSTDSR_b.XTALSTDF))
    {
        /* Clear the XTAL STD flag */
        WRITE_REG32(bCM_CMU->XTALSTDSR_b.XTALSTDF, 0UL);
    }
}

/**
 * @brief  Get the Xtal error flag.
 * @param  None
 * @retval en_flag_status_t
 */
en_flag_status_t CLK_GetXtalStdStatus(void)
{
    return ((0UL != READ_REG32(bCM_CMU->XTALSTDSR_b.XTALSTDF)) ? Set : Reset);
}

/**
 * @brief  Get the clock stable flag.
 * @param  [in] u8Flag              Specifies the clock stable flag.
 *          This parameter can be one of the following values:
 *   @arg CLK_FLAG_HRCSTB   :    Hrc clock stable flag
 *   @arg CLK_FLAG_XTALSTB  :    Xtal clock stable flag
 *   @arg CLK_FLAG_XTAL32STB:    Xtal32 clock stable flag
 * @retval en_flag_status_t
 */
en_flag_status_t CLK_GetStableStatus(uint8_t u8Flag)
{
    /* Check parameters */
    DDL_ASSERT(IS_CLK_STB_FLAG(u8Flag));

    return ((0U != (READ_REG8_BIT(CM_CMU->OSCSTBSR, u8Flag)))? Set : Reset);
}

/**
 * @}
 */

#endif /* DDL_CLK_ENABLE */

/**
 * @}
 */

/**
* @}
*/

/******************************************************************************
 * EOF (not truncated)
 *****************************************************************************/
